import tensorflow as tf

'''
卷积函数的用法
手动生成一个5 x 5的矩阵模拟图片，定义2 x 2 的卷积核
'''

# 定义三个输入变量模拟图片
# [batch, in_height, in_width, in_channels]
# ['训练时一个batch的图像数量','图片高度', '图片宽度', '图像通道数']
input1 = tf.Variable(tf.constant(1., shape=[1, 5, 5, 1]))
# [
#  [
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#  ]
# ]
input2 = tf.Variable(tf.constant(1., shape=[1, 5, 5, 2]))
# [
#  [
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   ]
# ]

input3 = tf.Variable(tf.constant(1., shape=[1, 4, 4, 1]))
# [
#  [
#   [[1.] [1.] [1.] [1.]]
#   [[1.] [1.] [1.] [1.]]
#   [[1.] [1.] [1.] [1.]]
#   [[1.] [1.] [1.] [1.]]
#   ]
#  ]

# 定义卷积核
'''
定义5个卷积核，每个卷积核都是 2 x 2的矩阵，只是输入、输出的通道数有差别
分别为 1ch输入、1ch输出、1ch输入、2ch输出， 1ch输入、3ch输出，
2ch输入、2ch输出、2ch输入、1ch输出，并分别在里面填入指定的值
'''
# [filter_height, filter_width, in_channels, out_channels]
# ['卷积核的高度','卷积核的宽度','图像通道数','滤波器个数']
filter1 = tf.Variable(tf.constant([-1., 0, 0, -1], shape=[2, 2, 1, 1]))
# [
#  [
#   [[-1.]] [[ 0.]]
#  ]
#  [
#   [[ 0.]] [[-1.]]
#  ]
# ]


filter2 = tf.Variable(tf.constant([-1., 0, 0, -1,
                                   -1., 0, 0, -1], shape=[2, 2, 1, 2]))
# [
#   [
#     [[-1. 0.]]
#     [[0. -1.]]
#   ]
#   [
#     [[-1. 0.]]
#     [[0. -1.]]
#   ]
# ]

filter3 = tf.Variable(tf.constant([-1., 0, 0, -1,
                                   -1., 0, 0, -1,
                                   -1., 0, 0, -1], shape=[2, 2, 1, 3]))
# [
#  [
#   [[-1.  0. 0.]]
#   [[-1. -1. 0.]]
#  ]
#  [
#   [[0. -1. -1.]]
#   [[0.  0. -1.]]
#  ]
# ]

filter4 = tf.Variable(tf.constant([-1., 0, 0, -1,
                                   -1., 0, 0, -1,
                                   -1., 0, 0, -1,
                                   -1., 0, 0, -1], shape=[2, 2, 2, 2]))  # 2*2*2*2 = 16
# [
#  [
#   [[-1.  0.] [ 0. -1.]]
#   [[-1.  0.] [ 0. -1.]]
#  ]
#  [
#   [[-1.  0.] [ 0. -1.]]
#   [[-1.  0.] [ 0. -1.]]
#  ]
# ]


filter5 = tf.Variable(tf.constant([-1., 0, 0, -1,
                                   -1., 0, 0, -1], shape=[2, 2, 2, 1]))
# [
#  [
#   [[-1.] [ 0.]]
#   [[ 0.] [-1.]]
#  ]
#  [
#   [[-1.] [ 0.]]
#   [[ 0.] [-1.]]
#  ]
# ]

# 定义卷积操作

# 将上面的两步组合起来，建立8个卷积操作
# padding 值为 'VALID' 的，表示边缘不填充
# padding 值为 'SAME'  的，表示便于边缘填充到卷积核可以达到图像的边缘

# 1 个通道输入，1个 feature map 生成
op1 = tf.nn.conv2d(input1, filter1, strides=[1, 2, 2, 1], padding='SAME')
# [
#  [
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#  ]
# ]

# [
#  [
#   [[-1.]]
#   [[ 0.]]
#  ]
#  [
#   [[ 0.]]
#   [[-1.]]
#  ]
# ]

# [array([[
#         [[-2.],[-2.],[-1.]],
#         [[-2.],[-2.],[-1.]],
#         [[-1.],[-1.],[-1.]]
#         ]], dtype=float32),
# 1 个通道输入，2个 feature map 生成
op2 = tf.nn.conv2d(input1, filter2, strides=[1, 2, 2, 1], padding='SAME')
# [
#  [
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#  ]
# ]

# [
#   [
#     [[-1. 0.]]
#     [[0. -1.]]
#   ]
#   [
#     [[-1. 0.]]
#     [[0. -1.]]
#   ]
# ]

# array([[
#         [[-2., -2.],[-2., -2.],[-2.,  0.]],
#         [[-2., -2.],[-2., -2.],[-2.,  0.]],
#         [[-1., -1.],[-1., -1.],[-1.,  0.]]
#       ]], dtype=float32),
# 1 个通道输入，3个 feature map 生成
op3 = tf.nn.conv2d(input1, filter3, strides=[1, 2, 2, 1], padding='SAME')

# [
#  [
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#  ]
# ]

# [
#  [
#   [[-1.  0. 0.]]
#   [[-1. -1. 0.]]
#  ]
#  [
#   [[0. -1. -1.]]
#   [[0.  0. -1.]]
#  ]
# ]

# array([[
#         [[-2., -2., -2.],[-2., -2., -2.],[-1., -1., -1.]],
#         [[-2., -2., -2.],[-2., -2., -2.],[-1., -1., -1.]],
#         [[-2., -1.,  0.],[-2., -1.,  0.],[-1.,  0.,  0.]]
#       ]], dtype=float32)
# 2 个通道输入，2个 feature map 生成
op4 = tf.nn.conv2d(input2, filter4, strides=[1, 2, 2, 1], padding='SAME')

# [
#  [
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   ]
# ]

# [
#  [
#   [[-1.  0.] [ 0. -1.]]
#   [[-1.  0.] [ 0. -1.]]
#  ]
#  [
#   [[-1.  0.] [ 0. -1.]]
#   [[-1.  0.] [ 0. -1.]]
#  ]
# ]


# 2 个通道输入，1个 feature map 生成
op5 = tf.nn.conv2d(input2, filter5, strides=[1, 2, 2, 1], padding='SAME')
# [
#  [
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   [[1. 1.] [1. 1.] [1. 1.] [1. 1.] [1. 1.]]
#   ]
# ]

# [
#  [
#   [[-1.] [ 0.]]
#   [[ 0.] [-1.]]
#  ]
#  [
#   [[-1.] [ 0.]]
#   [[ 0.] [-1.]]
#  ]
# ]

# 5 * 5对于padding不同而不同
vop1 = tf.nn.conv2d(input1, filter1, strides=[1, 2, 2, 1], padding="VALID")
# [
#  [
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#   [[1] [1] [1] [1] [1]]
#  ]
# ]

# [
#  [
#   [[-1.]] [[ 0.]]
#  ]
#  [
#   [[ 0.]] [[-1.]]
#  ]
# ]

op6 = tf.nn.conv2d(input3, filter1, strides=[1, 2, 2, 1], padding="SAME")

# [
#  [
#   [[1.] [1.] [1.] [1.]]
#   [[1.] [1.] [1.] [1.]]
#   [[1.] [1.] [1.] [1.]]
#   [[1.] [1.] [1.] [1.]]
#   ]
#  ]

# [
#  [
#   [[-1.]] [[ 0.]]
#  ]
#  [
#   [[ 0.]] [[-1.]]
#  ]
# ]


# 4 * 4 与 padding 无关
vop6 = tf.nn.conv2d(input3, filter1, strides=[1, 2, 2, 1], padding="VALID")

# [
#  [
#   [[1.] [1.] [1.] [1.]]
#   [[1.] [1.] [1.] [1.]]
#   [[1.] [1.] [1.] [1.]]
#   [[1.] [1.] [1.] [1.]]
#   ]
#  ]

# [
#  [
#   [[-1.]] [[ 0.]]
#  ]
#  [
#   [[ 0.]] [[-1.]]
#  ]
# ]

# 进行卷及操作
init = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(init)

    print('op1:\n', sess.run([op1, filter1]))  # 1-1 后补0
    print('---------------------------------------------')

    print('op2:\n', sess.run([op2, filter2]))  # 1-2 多卷积核， 按列取
    print('op3:\n', sess.run([op3, filter3]))  # 1-3 一个输入， 3个输出
    print('---------------------------------------------')

    print('op4:\n', sess.run([op4, filter4]))  # 2-2 通道叠加
    print('op5:\n', sess.run([op5, filter5]))  # 2-1 两个输入，一个输出
    print('---------------------------------------------')

    print('op1:\n', sess.run([op1, filter1]))  # 1-1 一个输出，一个输入
    print('vop1:\n', sess.run([vop1, filter1]))  #
    print('op6:\n', sess.run([op6, filter1]))  #
    print('vop6:\n', sess.run([vop6, filter1]))  #
